﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.VSSDK.Tools.VsIdeTesting;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Shell;
using EnvDTE;
using System.Diagnostics;
using UCL.Powersource;
using System.ComponentModel.Design;
using Microsoft.VsSDK.IntegrationTestLibrary;
using PowersourceUIToolkit;
using UCL.Powersource.ViewModels;
using System.Threading;
using UCL.Powersource.TFS;
using UCL.Powersource.Helpers;

namespace Powersource_IntegrationTests
{
    /// <summary>
    /// Integration test for package validation
    /// </summary>
    [TestClass]
    public class PackageTest
    {



        private delegate void ThreadInvoker();

        private TestContext testContextInstance;

        /// <summary>
        ///Gets or sets the test Context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }


        PowersourceMainViewModel ViewModel = null;
        Dictionary<string, bool> chosenFields = new Dictionary<string, bool>();

        string DomainUri=null;
        string ProjectUri = null;
        
        ILog Log;

        [TestMethod]
        [HostType("VS IDE")]
        //[Ignore]
        public void PackageLoadTest()
        {
            IVsPackage ivspackage = null;
            PowersourcePackage package = null;

            LogManager.DefaultInstance.AddLog(new FileLog(VerbosityLevels.Debug,"D:\\temp\\logs\\Powersource.log"));
            Log=LogManager.GetLog(this.GetType());
            Log.TraceMethod("Test start");
            Guid packageGuid = new Guid(UCL.Powersource.GuidList.guidPowersourcePkgString);
            Log.Trace("PackageLoadTest Entry point on {0}", DateTime.Now);
            
            
            using (TestMutexActivated = new Mutex(false, "SyncMUTEXVSActive_TEST"))
            using (TestMutexConnected = new Mutex(false, "SyncMUTEXVSConnected_TEST"))
            {
                #region First UIThread Invoke
                UIThreadInvoker.Invoke((ThreadInvoker)delegate()
            {
                Log.Trace("PackageLoadTest Entry point on UI Thread");
                //Get the Shell Service
                IVsShell shellService = VsIdeTestHostContext.ServiceProvider.GetService(typeof(SVsShell)) as IVsShell;

                var s = shellService.LoadPackage(ref packageGuid, out ivspackage);
                Trace.TraceInformation("GUID:{0}\r\npackage:{1}\r\nresult:{2}\r\n", packageGuid, ivspackage, s);
                Assert.IsTrue(s == 0);
                Log.Trace("Package successfully loaded.");
                package = (PowersourcePackage)ivspackage;
                
                CommandID toolWindowCmd = new CommandID(UCL.Powersource.GuidList.guidPowersourceCmdSet, (int)UCL.Powersource.PkgCmdIDList.cmdidPowersourceToolwindow);

                TestUtils testUtils = new TestUtils();
                testUtils.ExecuteCommand(toolWindowCmd);
                Log.Trace(string.Format("ToolWindow Command executed. "));

                bool r=testUtils.CanFindToolwindow(new Guid(UCL.Powersource.GuidList.guidToolWindowPersistanceString));
                Log.Trace(string.Format("CanFindToolwindow = {0}. ",r));
                package.BootStrapper.Initialize();
                Log.Trace(string.Format("package.BootStrapper.IsInitialize = {0} ", package.BootStrapper.IsInitialized));
                //Getting the viewmodel
                
                Log.Trace("Log from logmanager available.");
                var PowersourceExt = PowersourceConnector.GetExtensibilityPoint();
                var PowersourceExtLog = PowersourceExt.Log;
                Log.Trace("PowersourceExt.Log available.");
                ViewModel = ViewModelLocator.GetViewModel<UCL.Powersource.ViewModels.PowersourceMainViewModel>();
                if (ViewModel == null) Assert.Fail("Cannot find MainViewModel!!");
                
                ViewModel.Activated += ViewModel_Activated;
                ViewModel.PropertyChanged += ViewModel_PropertyChanged;

                

                Trace.WriteLine(string.Format("Before wating for TestMutexActivated: {0}", TestMutexActivated));
                if (!TestMutexActivated.WaitOne(10000))
                {
                    Assert.Fail(string.Format("TestMutexActivated timeouted in UI Thread on {0}", DateTime.Now));
                };
                if (!ViewModel.IsTFSConnected)
                {
                    Debug.WriteLine("VM {0} is not connected (First UI call)", ViewModel.GetHashCode());
                    Assert.IsNotNull(TestMutexConnected);
                    if (!TestMutexConnected.WaitOne(10000))
                    {
                        Assert.Fail(string.Format("TestMutexConnected timeouted in UI Thread on {0}", DateTime.Now));
                    };
                    Debug.WriteLine("Test mutex passed, we are connected");
                    ViewModel.DisconnectFromTFS();
                    ViewModel.ConnectToTFS("http://81.2.210.131:8080/tfs/testprojectcollection1");
                }

                DomainUri = ViewModel.TFSExt.ActiveProjectContext.DomainUri;
                ProjectUri = ViewModel.TFSExt.ActiveProjectContext.ProjectUri;
                
                Trace.WriteLine(string.Format("Leaving UI thread. We are connected to VM {0} - In test thread we will wait for Activated and Connected events to release mutex", ViewModel.GetHashCode()));
            });
                #endregion
                
                #region Test waiting for activated and connected
                Trace.WriteLine(string.Format("In Test thread waiting for activated to release mutex"));
                if (!TestMutexActivated.WaitOne(10000))
                {
                    Assert.Fail(string.Format("TestMutexActivated timeouted in Test thread on {0}", DateTime.Now));
                };
                Trace.WriteLine(string.Format("In Test thread waiting for connected to release mutex"));
                if (!ViewModel.IsTFSConnected)
                {
                    //toreleaseMutexConnectedIn2ndCallToUIThread = false;  //As we are going to wait for Mutex the second call will not release the lock
                    if (TestMutexConnected == null)
                    {
                        Assert.Fail("TestMutexConnected == null");
                    }
                    if (!TestMutexConnected.WaitOne(10000))
                    {
                        Assert.Fail(string.Format("TestMutexConnected timeouted in Test thread on {0}", DateTime.Now));
                    };
                    TestMutexConnected.ReleaseMutex();
                }
                TestMutexActivated.ReleaseMutex();
                
                Trace.WriteLine("Releasing both mutexes in test thread. Leaving Test thread, invoking UI thread for the second time");
                #endregion

                

                #region Second UIThread Invoke

                UIThreadInvoker.Invoke((ThreadInvoker)delegate()
                {
                    
                    Trace.WriteLine(string.Format("In UI thread - 2nd invoke. The first VM [{0} ,hc={1}, isActivated={2},isConnected={3}]", ViewModel, ViewModel.GetHashCode(), ViewModel.IsActivated, ViewModel.IsTFSConnected));
                    Assert.IsTrue(ViewModel.IsActivated, "View is not active!");
                    Assert.IsTrue(ViewModel.IsTFSConnected, "TFS Not connected, no reason to continue...");

                    DomainUri = ViewModel.TFSExt.ActiveProjectContext.DomainUri;
                    ProjectUri = ViewModel.TFSExt.ActiveProjectContext.ProjectUri;

                    Trace.WriteLine(string.Format("View is active and opened. TFS is connected. QUerying PackageConfiguration"));
                    var tl = ViewModel.PackageConfiguration;
                    Assert.IsNotNull(tl, "VM.PackageConfiguration is null. Unable to get configurations");

                    var tcConfigurations = tl.TFSTeamCollectionConfigurations;
                    Assert.IsNotNull(tcConfigurations, "VM.PackageConfiguration.TFSTeamCollectionConfigurations is null. Unable to get configurations");
                    Trace.WriteLine("Entering the loop over configurations, saving the Context");
                    foreach (var tcc in tcConfigurations)
                    {
                        Assert.IsNotNull(tcc, string.Format("tcc.TeamProjectConfigurations for tcc={0} is null. Unable to get configurations", tcc.Uri));
                        foreach (var tpc in tcc)
                        {
                            ((TfsTeamProjectConfiguration)tpc).IsChosen = !tpc.IsChosen;
                            Trace.WriteLine(string.Format("{0} - {1} ", tcc.Uri + tpc.UniqueName, tpc.IsChosen));
                            chosenFields.Add(tcc.Uri + tpc.UniqueName, tpc.IsChosen);
                        }
                    }
                    Log.Trace("Disconnecting from TFS");
                    ViewModel.DisconnectFromTFS();
                    Log.Trace("Deactivating the view model");
                    ViewModel.Deactivate();
                    
                    Assert.IsFalse(ViewModel.IsActivated, "ViewModel was not deactivated");
                    Assert.IsFalse(ViewModel.IsTFSConnected, "ViewModel was not disconnected");
                    Assert.IsNull(ViewModel.PackageConfiguration, "ViewModel PackageConfiguration is not null, Loading will not be performed");
                    ViewModel.ConnectToTFS(DomainUri,ProjectUri);
                    ViewModel.Activate();
                    Assert.IsTrue(ViewModel.IsActivated, "ViewModel was not activated");
                    Assert.IsTrue(ViewModel.IsTFSConnected, "ViewModel was not connected");
                    Trace.WriteLine("Everything ready for running the second stage . Getting the service...");
                    IVsShell shellService = VsIdeTestHostContext.ServiceProvider.GetService(typeof(SVsShell)) as IVsShell;
                    Assert.IsTrue(shellService != null, "shellService not available");
                    Trace.WriteLine("Getting the package again...");
                    var s = shellService.LoadPackage(ref packageGuid, out ivspackage);
                    Trace.TraceInformation("Second get of package GUID:{0}, package:{1} with result:{2}\r\n", packageGuid, ivspackage, s);
                    Assert.IsTrue(s == 0);
                    package = ivspackage as PowersourcePackage;
                    Assert.IsFalse(package.IsDisposed, "Package is disposed. You cannot continue");
                    
                    Assert.IsTrue(ViewModel.IsTFSConnected, "TFS Not connected, no reason to continue...");
                    tl = ViewModel.PackageConfiguration;
                    Assert.IsNotNull(tl, "VM.PackageConfiguration is null. Unable to get configurations");
                    tcConfigurations = tl.TFSTeamCollectionConfigurations;
                    Assert.IsNotNull(tcConfigurations, "VM.PackageConfiguration.TFSTeamCollectionConfigurations is null. Unable to get configurations");
                    Trace.WriteLine("Entering the loop over configurations - Test phase");
                    foreach (var tcc in tcConfigurations)
                    {
                        Assert.IsNotNull(tcc, string.Format("tcc.TeamProjectConfigurations for tcc={0} is null. Unable to get configurations", tcc.Uri));
                        foreach (var tpc in tcc)
                        {
                            bool? isChosenFromDict = null;
                            if (chosenFields.ContainsKey(tcc.Uri + tpc.UniqueName))
                            {
                                isChosenFromDict = chosenFields[tcc.Uri + tpc.UniqueName];
                            }
                            Trace.WriteLine(string.Format("{0} - {1} and loaded={2}", tcc.Uri + tpc.UniqueName, tpc.IsChosen, isChosenFromDict));
                            Assert.IsTrue(isChosenFromDict == tpc.IsChosen, string.Format("{0} - IsChosen= {1} does not match loaded IsChosen={2}", tcc.Uri + tpc.UniqueName, tpc.IsChosen, isChosenFromDict));
                        }
                    }
                  
                });

                #endregion

               
            }
        }

        void ViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            Trace.TraceInformation("ViewModel property {0} changed", e.PropertyName);
            if (e.PropertyName == PropertyExtensions.GetPropertyName(() => ViewModel.IsTFSConnected))
            {
                if (ViewModel.IsTFSConnected)
                {
                    TestMutexConnected.ReleaseMutex();
                }
            }
        }


        Mutex TestMutexActivated;
        Mutex TestMutexConnected;

        void ViewModel_Activated(object sender, EventArgs e)
        {
            Trace.WriteLine(string.Format("Dettaching the vm_activated listener for VM = {0}", ViewModel.GetHashCode()));
            this.ViewModel.Activated -= ViewModel_Activated;
            Trace.WriteLine("Releasing Mutex in vm_Activated... ");
            if (TestMutexActivated == null)
            {
                Assert.Fail("TestMutexActivated is null!!");
            }
            TestMutexActivated.ReleaseMutex();
            Trace.WriteLine("Mutex in vm_Activated released.");
        }
    }
}
